﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Nature
{
    [DebuggerDisplay("C: {Carbon}, H: {Hydrogen}, O: {Oxygen}")]
    [Serializable]
    public class CHOFormula
    {
        /// <summary>
        /// Creates a new instance of the CHOFormula class
        /// </summary>
        /// <param name="c">Total number of the Carbon atoms</param>
        /// <param name="h">Total number of the Hidrogen atoms</param>
        /// <param name="o">Total number of the Oxygen atoms</param>
        public CHOFormula(double c, double h, double o)
        {
            Carbon = c;
            Hydrogen = h;
            Oxygen = o;
        }

        /// <summary>
        /// Creates a new instance of the CHOFormula class
        /// </summary>
        /// <param name="mixture"></param>
        public CHOFormula(IdealGasMixture mixture)
        {
            Carbon = 0.0;
            Hydrogen = 0.0;
            Oxygen = 0.0;

            int indexC = mixture.Model.GetElementIndex("c");
            int indexO = mixture.Model.GetElementIndex("o");
            int indexH = mixture.Model.GetElementIndex("h");

            var x = mixture.SpeciesMoleFractions.Data;            
            var elsp = mixture.Model.ElspMatrix;            
            if (indexC >= 0)
            {
                var crow = elsp[indexC].Data;
                for (int isp = 0; isp < mixture.Nsp; ++isp) 
                {
                    Carbon += x[isp] * crow[isp];                
                }
            }
            if (indexH >= 0)
            {
                var hrow = elsp[indexH].Data;
                for (int isp = 0; isp < mixture.Nsp; ++isp)
                {
                    Hydrogen += x[isp] * hrow[isp];
                }
            }
            if (indexO >= 0)
            {
                var orow = elsp[indexO].Data;
                for (int isp = 0; isp < mixture.Nsp; ++isp)
                {
                    Oxygen += x[isp] * orow[isp];
                }
            }
        }

        #region Private Methods
        private static void Swap<T>(ref T a, ref T b)
        {
            T tmp = a;
            a = b;
            b = tmp;
        } 
        #endregion

        #region Public Properties

        public double Carbon { get; private set; }

        public double Hydrogen { get; private set; }

        public double Oxygen { get; private set; }

        public double OxygenBalance
        {
            get
            {
                return Oxygen - 2.0 * Carbon - Hydrogen / 2.0;
            }
        }

        public bool IsOxydizer { get { return (this.OxygenBalance > 0.0); } }

        public bool IsFuel { get { return (this.OxygenBalance < 0.0); } }

        public bool IsBalanced { get { return this.OxygenBalance == 0.0; } } 

        #endregion

        public static double CalcEquivalenceRatio(
            CHOFormula comp1, double x1,
            CHOFormula comp2, double x2)
        {
            if (comp1.IsOxydizer)
            {
                Swap(ref comp1, ref comp2);
                Swap(ref x1, ref x2);
            }            
            return CalcEquivalenceRatio(comp1, comp2, x1);
        }

        public static double CalcEquivalenceRatio(
            CHOFormula compFuel, CHOFormula compOxydizer, double xFuel)
        {
            return CalcEquivalenceRatio(compFuel.OxygenBalance, compOxydizer.OxygenBalance, xFuel);
        }

        public static double CalcEquivalenceRatio(
            double fuOxBalance, double oxOxBalance, double xFuel)
        {
            if (fuOxBalance >= 0.0 || oxOxBalance <= 0.0)
            {
                throw new InvalidOperationException();
            }
            double n_ox_st = (-fuOxBalance) / oxOxBalance;
            double n_ox = (1.0 / xFuel - 1.0);
            return n_ox_st / n_ox;
        }

        public static double CalcMixtureFraction(
            CHOFormula fr1, double y1,
            CHOFormula fr2, double y2)
        {
            double oxBalance1 = fr1.OxygenBalance;
            double oxBalance2 = fr2.OxygenBalance;
            if (oxBalance1 < 0.0 && oxBalance2 > 0.0 ) { return y1; }
            else if (oxBalance1 > 0.0 && oxBalance2 < 0.0) { return y2; }
            throw new InvalidOperationException();
        }


        public static double CalcFuelMoleFraction
            (
                double fuOxBalance,
                double oxOxBalance,
                EquivalenceRatio ratio
            )
        {
            double n_ox_st = (-fuOxBalance) / oxOxBalance;
            double n_ox = n_ox_st / ratio.Value;
            double n_fu = 1.0;
            if (double.IsPositiveInfinity(n_ox)) { return 0.0; }
            double n_sum = n_fu + n_ox;
            return n_fu / n_sum;
        }

        public static double CalcFuelMoleFraction
            (
                CHOFormula f1,
                CHOFormula f2,
                EquivalenceRatio ratio
            )
        {
            double b1 = f1.OxygenBalance;
            double b2 = f2.OxygenBalance;
            if (b1 < 0.0 && b2 > 0.0)
            {
                return CalcFuelMoleFraction(b1, b2, ratio);
            }
            else if (b2 < 0.0 && b1 > 0.0)
            {
                return CalcFuelMoleFraction(b2, b1, ratio); 
            }
            throw new InvalidOperationException();
        }
    }
}
